#!/usr/bin/python3
# -*- coding: utf-8 -*-

import sys, os
sys.path.append(os.path.dirname(os.path.dirname(__file__)))

import download_client, db
import threading, logging
import filetype

from fdfs_client import client
from settings import config
from datetime import datetime

def dfs_client():
    tracker_path = client.tracker_conf(config['fdfs'])
    return client.Fdfs_client(tracker_path)

def upload_parse(filename, path=None):
    print('upload_parse')
    try:
        if isinstance(filename, bytes):
            ret = dfs_client().upload_by_buffer(filename)
        if isinstance(filename, str):
            ret = dfs_client().upload_by_filename(filename)
    except BaseException as e:
        logging.error('Error: %s' % e)
        return {'status': False, 'msg': e}
    group_name = ret.get('Group name', b'').decode()
    file_id = ret.get('Remote file_id', b'').decode()
    # Upload successed.
    status = ret.get('Status')
    # filepath = ret.get('Local file name')
    # 155.19KB
    size = ret.get('Uploaded size')
    if 'KB' in size:
        size = float(size[:-2]) * 1024
    elif 'MB' in size:
        size = float(size[:-2]) * 1024 * 1024
    elif 'GB' in size:
        size = float(size[:-2]) * 1024 * 1024 * 1024
    elif 'TB' in size:
        size = float(size[:-2]) * 1024 * 1024 * 1024 * 1024
    elif 'PB' in size:
        size = float(size[:-2]) * 1024 * 1024 * 1024 * 1024 * 1024
    elif 'EB' in size:
        size = float(size[:-2]) * 1024 * 1024 * 1024 * 1024 * 1024 * 1024
    elif 'ZB' in size:
        size = float(size[:-2]) * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024
    elif 'YB' in size:
        size = float(size[:-2]) * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024
    else:
        size = float(size[:-1])
    ip = ret.get('Storage IP', b'').decode()
    logging.info('[+]group_name: %s, file_id: %s, status: %s, size: %s, ip: %s' % (group_name, file_id, status, size, ip))
    logging.info('%s------------------>[Upload: %s]' % (threading.current_thread().name, status))
    if path:
        filepath = filename
        filename = filename.rsplit('/', 1)[1] if '/' in filename else filename
        session = db.create_session()
        fdfs = session.query(db.Fdfs).filter_by(filename=filename, path=path).first()
        if fdfs:
            if fdfs.content_type == '-':
                kind = filetype.guess(filepath)
                if kind is not None:
                    fdfs.content_type = kind.mime
            fdfs.group_name = group_name
            fdfs.file_id = file_id
            fdfs.status = ('successed' in status)
            fdfs.size = size
            fdfs.ip = ip
            fdfs.updated_at = datetime.now()
            logging.info('%s------------------->[Commit: %s]' % (threading.current_thread().name, fdfs))
            session.commit()
        else:
            logging.error('%s------------------->[Not found: %s/%s]' % (threading.current_thread().name, path, filename))

    return {"group_name": group_name, 'file_id': file_id, 'status': ('successed' in status), 'size': size, 'ip': ip}

def upload_by_filename(filename, path='/root', content_type=None):
    filepath = filename
    session = db.create_session()
    filename = filename.rsplit('/', 1)[1] if '/' in filename else filename
    if len([item for item in session.query(db.Fdfs).filter_by(filename=filename, path=path)]) > 0:
        return {'status': False, 'msg': '文件重名'}
    fdfs = db.Fdfs(filename=filename, path=path, content_type=content_type, download_count=0)
    logging.info('--------------> %s' % fdfs)
    session.add(fdfs)
    session.commit()
    # 子线程上传文件
    upload_parse_t = threading.Thread(target=upload_parse, name='upload_parse-%s/%s' % (path, filename), args=(filepath, path))
    upload_parse_t.setDaemon(True)
    upload_parse_t.start()
    # upload_parse_t.join()
    return {'status': True, 'msg': '文件上传中'}

def upload_by_buffer(buffer, filename, path='/root', content_type=None):
    filename = filename.rsplit('/', 1)[1] if '/' in filename else filename
    session = db.create_session()
    if len([item for item in session.query(db.Fdfs).filter_by(filename=filename, path=path)]) > 0:
        return {'status': False, 'msg': '文件重名'}
    upload = upload_parse(buffer)
    if upload['status']:
        fdfs = db.Fdfs(filename=filename, group_name=upload['group_name'], file_id=upload['file_id'], status=upload['status'], size=upload['size'], ip=upload['ip'], path=path, content_type=content_type, download_count=0)
        logging.info('-----------------> %s' % fdfs)
        session.add(fdfs)
        session.commit()
        return {'status': True, 'msg': '文件上传成功'}
    else:
        logging.error('failed to upload: %s' % filename)
        return {'status': False, 'msg': '文件保存失败'}

def download(filename, file_id):
    try:
        ret = dfs_client().download_to_file(filename, file_id.encode('utf-8'))
        # print('--> %s' % ret)
        file_id = ret.get('Remote file_id', b'').decode()
        # filename = ret.get('Content')
        # 155.19KB
        size = ret.get('Download size')
        ip = ret.get('Storage IP', b'').decode()
        logging.info('[+]size: %s, file_id: %s, ip: %s' % (size, file_id, ip))
        session = db.create_session()
        fdfs = session.query(db.Fdfs).filter_by(file_id=file_id).first()
        if fdfs:
            fdfs.download_count = fdfs.download_count + 1
            session.commit()
            logging.info('下载成功: %s, %s' % (filename, file_id))
            return {'status': True, 'msg': '文件未记录[%s,%s]' % (filename, file_id)}
        return {'status': False, 'msg': '文件未记录[%s,%s]' % (filename, file_id)}
    except BaseException as e:
        logging.error('Error: %s' % e)
        return {'status': False, 'msg': e}

def delete_by_path(parent, name):
    session = db.create_session()
    fdfs = session.query(db.Fdfs).filter_by(filename=name, path=parent).first()
    if fdfs.content_type == 'd':
        dir_ = '%s/%s' % (parent, name)
        session.query(db.Fdfs).filter(db.Fdfs.path.like('%s%%' % dir_)).delete(synchronize_session=False)
        session.delete(fdfs)
        session.commit()
        return {'status': True, 'msg': '删除成功'}
    else:
        if fdfs.file_id:
            return delete(fdfs.file_id)
        else:
            session.delete(fdfs)
            session.commit()
            return {'status': True, 'msg': '删除成功'}

def delete(file_id):
    try:
        ret = dfs_client().delete_file(file_id.encode('utf-8'))
        logging.info('--> %s' % list(ret))
        # Delete file successed.
        status = ret[0]
        file_id = ret[1].decode()
        ip = ret[2].decode()
        logging.info('[+]status: %s, file_id: %s, ip: %s' % (status, file_id, ip))
        if 'successed' in status:
            session = db.create_session()
            fdfs = session.query(db.Fdfs).filter_by(file_id=file_id).first()
            if fdfs:
                session.delete(fdfs)
                session.commit()
            return {'status': True, 'msg': '删除成功'}
        else:
            return {'status': False, 'msg': '删除失败'}
    except BaseException as e:
        logging.error('Error: %s' % e)
        return {'status': False, 'msg': '删除失败: %s' % e}

def rename(fullpath, newname):
    session = db.create_session()
    parent, name = fullpath.rsplit('/', 1)
    if '/' in newname:
        return {'status': False, 'msg': '文件名[%s]包含"/"' % newname}
    fdfs = session.query(db.Fdfs).filter_by(filename=newname, path=parent).first()
    if fdfs:
        return {'status': False, 'msg': '路径[%s]下已存在[%s]' % (parent, newname)}
    fdfs = session.query(db.Fdfs).filter_by(filename=name, path=parent).first()
    if not fdfs:
        return {'status': False, 'msg': '文件[%s]不存在' % fullpath}
    if fdfs.content_type == 'd':
        child_files = session.query(db.Fdfs).filter(db.Fdfs.path.like('%s%%' % fullpath))
        for item in child_files:
            item.path = '%s/%s' % (parent, newname)
        fdfs.filename = newname
        session.commit()
    else:
        fdfs.filename = newname
        session.commit()
    return {'status': True, 'msg': '重命名成功'}

def move(fullpath, newpath):
    session = db.create_session()
    if newpath != '/root':
        parent, name = newpath.rsplit('/', 1)
        fdfs = session.query(db.Fdfs).filter_by(filename=name, path=parent).first()
        if not fdfs:
            return {'status': False, 'msg': '目标路径[%s]不存在' % newpath}
        if fdfs.content_type != 'd':
            return {'status': False, 'msg': '目标路径[%s]不是文件夹' % newpath}
    parent, name = fullpath.rsplit('/', 1)
    fdfs = session.query(db.Fdfs).filter_by(filename=name, path=newpath).first()
    if fdfs:
        {'status': False, 'msg': '目标路径[%s]已存在[%s]' % (newpath, name)}
    fdfs = session.query(db.Fdfs).filter_by(filename=name, path=parent).first()
    if not fdfs:
        return {'status': False, 'msg': '文件[%s]不存在' % fullpath}
    if fdfs.content_type == 'd':
        child_files = session.query(db.Fdfs).filter(db.Fdfs.path.like('%s%%' % fullpath))
        for item in child_files:
            item.path = '%s/%s' % (newpath, name)
        fdfs.path = newpath
        session.commit()
    else:
        fdfs.path = newpath
        session.commit()
    return {'status': True, 'msg': '移动成功'}

def offline_download(path, filename, url):
    if download_client.download_st(url, filename, 'tmp'):
        # 子线程上传文件
        upload_parse_t = threading.Thread(target=upload_parse, name='upload_parse-%s/%s' % (path, filename), args=('tmp/%s' % filename, path))
        upload_parse_t.setDaemon(True)
        upload_parse_t.start()
    else:
        logging.error('%s------------------->[Download Failed: %s]' % (threading.current_thread().name, url))

def offline(parent, url):
    try:
        session = db.create_session()
        name = url.rsplit('/', 1)[1] if '?' not in url.rsplit('/', 1)[1] else url.rsplit('/', 1)[1].split('?', 1)[0]
        if len([item for item in session.query(db.Fdfs).filter_by(filename=name, path=parent)]) > 0:
            return {'status': False, 'msg': '文件重名'}
        fdfs = db.Fdfs(status=True, filename=name, path=parent, content_type='-', referer=url)
        session.add(fdfs)
        session.commit()
        # 子线程上传文件
        upload_parse_t = threading.Thread(target=offline_download, name='offline_download-%s/%s' % (parent, name), args=(parent, name, url))
        upload_parse_t.setDaemon(True)
        upload_parse_t.start()
    except BaseException as e:
        return {'status': False, 'msg': e}
    return {'status': True, 'msg': '成功'}

def makedir(parent, name):
    try:
        session = db.create_session()
        if len([item for item in session.query(db.Fdfs).filter_by(filename=name, path=parent)]) > 0:
            return {'status': False, 'msg': '路径[%s/%s]已存在' % (parent, name)}
        fdfs = db.Fdfs(status=True, filename=name, path=parent, content_type='d')
        session.add(fdfs)
        session.commit()
    except BaseException as e:
        logging.error('Error: %s' % e)
        return {'status': False, 'msg': e}
    return {'status': True, 'msg': '成功'}

def goto_dir(path):
    session = db.create_session()
    data = [item.dobule_to_dict() for item in session.query(db.Fdfs).filter_by(path=path)]
    data.sort(key=lambda el: el['content_type'] != 'd')
    return {'files': data, 'path': path[1:].split('/')}

if __name__ == '__main__':
    help_msg = '''\
        Usage:python3 %s {option} filename [file_id]
        -u: filename         | upload filename
        -d: filename file_id | download filename by file_id
        -D: file_id          | Delelte filename by file_id
        ---------------------------------------------------------
        [require py3Fdfs]
        pip3 install SQLAlchemy==1.3.15 -i https://pypi.tuna.tsinghua.edu.cn/simple
        modify lib/python3.7/site-packages/fdfs_client/storage_client.py:105
        ---
        remain_bytes -= recv_size
        ---
        '''    % sys.argv[0]
    if len(sys.argv) < 2:
        print(help_msg)
    else:
        if '-u' == sys.argv[1]:
            filename = sys.argv[2]
            if not os.path.exists(filename):
                raise BaseException('file %s not exists' % filename)
            db.init_db_()
            result = upload_by_filename(filename)
            print(result)
            exit(0)
        if '-d' == sys.argv[1]:
            filename = sys.argv[2]
            file_id = sys.argv[3]
            db.init_db_()
            result = download(filename, file_id)
            print(result)
            exit(0)
        if '-D' == sys.argv[1]:
            file_id = sys.argv[2]
            db.init_db_()
            result = delete(file_id)
            print(result)
            exit(0)
        if '-t' == sys.argv[1]:
            db.init_db_()
            session = db.create_session()
            print([item for item in session.query(db.Fdfs).filter_by(filename='/1')])
            exit(0)
        print(help_msg)